﻿using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using ImageTagToolViewModel.ViewModels;
using System.Threading;
using ImageTagToolViewModel.Serialization;

namespace ImageTagToolViewModel.Models
{
    /// <summary>
    /// This class contains the logic of image processing.
    /// </summary>
    public class ImageProcessingModel
    {
        private MainWindowViewModel _MainWindowViewModel;
        public delegate void ActualPictureAppearedEventHandler(Picture actualPicture);
        /// <summary>
        /// Occurs, when a new ActualPicture appears for the first time in the current workflow.
        /// </summary>
        public event ActualPictureAppearedEventHandler ActualPictureAppeared;

        /// <summary>
        /// Creates a new instance of the ImageProcessingModel.
        /// </summary>
        /// <param name="mainWindowViewModel">The view-model to assign with.</param>
        public ImageProcessingModel(MainWindowViewModel mainWindowViewModel)
        {
            _MainWindowViewModel = mainWindowViewModel;
        }

        /// <summary>
        /// Scans the specified folder to acquire image data.
        /// </summary>
        /// <param name="path">The path of the folder.</param>
        public void ScanFolder(string path)
        {
            LocationDatabase.Instance.Add(path);
            List<string> foundImages = FileHandler.ScanForPictures(path);
            //foundImages.Sort((f1, f2) => f1.CompareTo(f2));

            _MainWindowViewModel.ContextRelatedMenuItemsEnabled = false;

            // Processes the pictures in background, so that the Main Window won't freeze out.
            _MainWindowViewModel.LoadProgressBarVisibility = true;
            int counter = 0;
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (object o, DoWorkEventArgs ea) =>
            {
                Parallel.ForEach(foundImages, f =>
                {
                    Picture picture = FileHandler.AnalyzePicture(f);
                    PictureHandler.Instance.Pictures.Add(picture);

                    counter++;
                    (o as BackgroundWorker).ReportProgress((int)(100.0f * counter / foundImages.Count));
                });
            };

            worker.ProgressChanged += (object o, ProgressChangedEventArgs ea) =>
            {
                _MainWindowViewModel.LoadProgressBarValue = ea.ProgressPercentage;
            };

            worker.RunWorkerCompleted += (object o, RunWorkerCompletedEventArgs ea) =>
            {
                // Waiting for Parallel.ForEach to end all of its threads.
                // Sometimes, when processing a lot of file, the foreach does not finish when this is called.
                while (PictureHandler.Instance.Pictures.Count < foundImages.Count)
                    Thread.Sleep(10);

                // Setting up UI layout.
                _MainWindowViewModel.LoadProgressBarVisibility = false;
                _MainWindowViewModel.LoadProgressBarValue = 0;
                _MainWindowViewModel.ContextRelatedMenuItemsEnabled = true;

                // Ordering pictures according to their full names.
                PictureHandler.Instance.Pictures.Sort((p1, p2) => p1.FileName.CompareTo(p2.FileName));

                if (foundImages.Count > 0)
                {
                    _MainWindowViewModel.MainWindow.ShowPicture(PictureHandler.Instance.Pictures[0].FileName);
                    PictureHandler.Instance.ActualPictureIndex = 0;
                    _MainWindowViewModel.NextButtonIsEnabled = true;
                    _MainWindowViewModel.PreviousButtonIsEnabled = false;
                }
                else
                {
                    _MainWindowViewModel.MainWindow.ShowMessageBox("No pictures were found.");
                    PictureHandler.Instance.ActualPictureIndex = -1;
                    _MainWindowViewModel.NextButtonIsEnabled = false;
                    _MainWindowViewModel.PreviousButtonIsEnabled = false;
                }

                if (PictureHandler.Instance.ActualPicture != null)
                {
                    _MainWindowViewModel.NameTextBoxText = PictureHandler.Instance.ActualPicture.Name;
                    _MainWindowViewModel.RatingSliderValue = 3.0;

                    // Notifying all components, that the ActualPicture became available.
                    if (ActualPictureAppeared != null)
                        ActualPictureAppeared(PictureHandler.Instance.ActualPicture);
                }
                _MainWindowViewModel.RefreshStatusBar();

                /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                SerializationProvider.SaveData();
            };

            worker.WorkerReportsProgress = true;
            worker.RunWorkerAsync();
        }

        public void AddOtherLocation(string path)
        {
            if (!LocationDatabase.Instance.CanAddLocation(path))
            {
                _MainWindowViewModel.MainWindow.ShowMessageBox("The given location is already scanned");
                return;
            }

            List<string> foundImages = FileHandler.ScanForPictures(path);
            if (foundImages.Count == 0)
            {
                _MainWindowViewModel.MainWindow.ShowMessageBox("No further pictures were found.");
                return;
            }

            LocationDatabase.Instance.Add(path);
            Picture actualPicture = PictureHandler.Instance.ActualPicture;

            _MainWindowViewModel.ContextRelatedMenuItemsEnabled = false;

            // Processes the pictures in background, so that the Main Window won't freeze out.
            _MainWindowViewModel.LoadProgressBarVisibility = true;
            int counter = 0;
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (object o, DoWorkEventArgs ea) =>
            {
                Parallel.ForEach(foundImages, f =>
                {
                    Picture picture = FileHandler.AnalyzePicture(f);
                    PictureHandler.Instance.Pictures.Add(picture);

                    counter++;
                    (o as BackgroundWorker).ReportProgress((int)(100.0f * counter / foundImages.Count));
                });
            };

            worker.ProgressChanged += (object o, ProgressChangedEventArgs ea) =>
            {
                _MainWindowViewModel.LoadProgressBarValue = ea.ProgressPercentage;
            };

            worker.RunWorkerCompleted += (object o, RunWorkerCompletedEventArgs ea) =>
            {
                // Waiting for Parallel.ForEach to end all of its threads.
                // Sometimes, when processing a lot of file, the foreach does not finish when this is called.
                while (PictureHandler.Instance.Pictures.Count < foundImages.Count)
                    Thread.Sleep(10);

                // Setting up UI layout.
                _MainWindowViewModel.LoadProgressBarVisibility = false;
                _MainWindowViewModel.LoadProgressBarValue = 0;
                _MainWindowViewModel.ContextRelatedMenuItemsEnabled = true;

                // Ordering pictures according to their full names.
                PictureHandler.Instance.Pictures.Sort((p1, p2) => p1.FileName.CompareTo(p2.FileName));

                // Restoring actual picture.
                int index = PictureHandler.Instance.Pictures.IndexOf(actualPicture);
                PictureHandler.Instance.ActualPictureIndex = index;
                _MainWindowViewModel.NextButtonIsEnabled = index < PictureHandler.Instance.Pictures.Count - 1;
                _MainWindowViewModel.PreviousButtonIsEnabled = index > 0;

                _MainWindowViewModel.RefreshStatusBar();
            };

            worker.WorkerReportsProgress = true;
            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Tries to opens last workflow, if pictures.xml exists.
        /// </summary>
        public void Open()
        {
            if (!FileHandler.FileExists(SerializationProvider.Path))
                return;

            SerializationProvider.LoadData();

            // Setting up layout.
            _MainWindowViewModel.ContextRelatedMenuItemsEnabled = true;
            _MainWindowViewModel.SetUIElementsEnabled(true);
            _MainWindowViewModel.NameTextBoxText = PictureHandler.Instance.ActualPicture.Name;
            _MainWindowViewModel.RatingSliderValue = PictureHandler.Instance.ActualPicture.Rating;

            _MainWindowViewModel.MainWindow.ShowPicture(PictureHandler.Instance.ActualPicture.FileName);
            _MainWindowViewModel.NextButtonIsEnabled = true;
            _MainWindowViewModel.PreviousButtonIsEnabled = false;

            // Notifying all components, that the ActualPicture became available.
            if (ActualPictureAppeared != null)
                ActualPictureAppeared(PictureHandler.Instance.ActualPicture);
            _MainWindowViewModel.RefreshStatusBar();
        }
    }
}
